package panels;

import interfaces.Renderer;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

import javax.swing.JComponent;

import sprites.Sprite;
import util.DrawSet;
import util.util.DDict;

public class MapPanel extends my.MyPanel<String> implements Renderer {

	public static final int	LEFT_BUTTON		= MouseEvent.BUTTON1;
	public static final int CENTER_BUTTON	= MouseEvent.BUTTON2;
	public static final int RIGHT_BUTTON	= MouseEvent.BUTTON3;
	
	public static final String	SPRITE_REMOVED_PROPERTY		= "SPRITE_REMOVED_PROPERTY";
	
	public static final Color	defaultGridColor = Color.black;
	
	public MapPanel() {
		super();
		
		setEnabled(true);
		setVisible(true);
		setDebug(true);
		setGridColor(defaultGridColor);
	}
	/**
	 * Map View
	 */
	private static final long serialVersionUID = 1L;
	
	public Set<Point>				selected		= DrawSet.emptySet();
	public Set<Point>				filled			= DrawSet.emptySet();
	public Sprite					selection		= null;
	
	protected boolean	moveCanvas = false;
	protected boolean	moveActors = false;
	protected boolean	selectTiles = false;
	
	protected boolean	drawMouse = false;
	
	protected double	zoom		= 25D;
	protected double	origonX		= 0D;
	protected double	origonY		= 0D;

	public double	toCanvasX(double screenX)		{	return	(screenX-(getPaintComponent().getWidth()/2D))/zoom - origonX;		}
	public double	toCanvasY(double screenY)		{	return	(screenY-(getPaintComponent().getHeight()/2D))/zoom - origonY;		}
	public double	toScreenX(double canvasX)		{	return	(canvasX + origonX) * zoom + getPaintComponent().getWidth()/2D;		}
	public double	toScreenY(double canvasY)		{	return	(canvasY + origonY) * zoom + getPaintComponent().getHeight()/2D;	}

	public int		toTileX(int x)					{	return	(int) Math.floor(toCanvasX((double) x));		}
	public int		toTileY(int y)					{	return	(int) Math.floor(toCanvasY((double) y));		}
	
	public double	cameraX()						{	return	toCanvasX(0D);				}
	public double	cameraY()						{	return	toCanvasY(0D);				}
	public double	cameraW()						{	return	getPaintComponent().getWidth() / zoom;			}
	public double	cameraH()						{	return	getPaintComponent().getHeight() / zoom;			}
	public double	cameraArea()					{	return	cameraW() * cameraH();		}
	
	protected DDict<Integer,Point2D>		mCanvasDown	= new DDict<Integer, Point2D>(new Point(0,0)); 
	protected DDict<Integer,Point2D>		mCanvasUp	= new DDict<Integer, Point2D>(new Point(0,0));
	protected Point2D						mCanvasPos	= new Point(0,0);

	protected DDict<Integer,Point2D>		mOrigonDown	= new DDict<Integer, Point2D>(new Point(0,0)); 
	protected DDict<Integer,Point2D>		mOrigonUp	= new DDict<Integer, Point2D>(new Point(0,0));
	protected Point2D						mOrigonPos	= new Point(0,0);
	
	private Color	gridColor 		= Color.black;
	private Color	selectedColor	= Color.white;
	
	private JComponent	paintComponent = this;
	
	
	//
	// Getters and setters
	//
	public synchronized JComponent getPaintComponent() {
		return paintComponent;
	}
	public synchronized void setPaintComponent(JComponent paintComponent) {
		this.paintComponent = paintComponent;
	}
	public Color getSelectedColor() {
		return selectedColor;
	}
	public void setSelectedColor(Color selectedColor) {
		this.selectedColor = selectedColor;
	}
	public Color getGridColor() {
		return gridColor;
	}
	public void setGridColor(Color gridColor) {
		this.gridColor = gridColor;
	}
	public double	getZoom() {
		return zoom;
	}
	public void setZoom(double value) {
		zoom = Math.max(1D, value);
		refresh();
	}
	public Point2D	getOrigon() {
		return new Point(
			(int) origonX, (int) origonY
		);
	}
	public void setOrigon(double x, double y) {
		origonX = x;
		origonY = y;
	}

	//
	// Internal methods
	//
	
	public Point2D	getDrag(int x, int y, int button) {
		return new Point(
			(int) (mScreenPos.getX() - ((Point) mScreenDown.get(button)).getX()),
			(int) (mScreenPos.getY() - ((Point) mScreenDown.get(button)).getY())
		);
	}
	
	public Rectangle	getTile(int x, int y) {
		return new Rectangle(
			(int) toScreenX(x),
			(int) toScreenY(y),
			(int) zoom,
			(int) zoom
		);
	}
	public Rectangle	getTile(Point2D P) 				{	return getTile((int) P.getX(), (int) P.getY());							}
	
	public Point 		toTile(int x, int y) 			{	return new Point( toTileX(x), toTileY(y) );								}
	public Point		toTile(Point2D P)				{	return toTile( (int) P.getX(), (int) P.getY());							}
	public Rectangle	screenTile(int x, int y)		{	return getTile(toTile(x,y));											}
	public Rectangle	screenTile(Point P)				{	return getTile(toTile(P.x, P.y));										}
	
	//
	// Canvas Painting Methods
	//
	public void drawMouse(Graphics G, Rectangle R) {
		if (isDrawMouse()) {
			int	headSize = Math.min(R.width, R.height)/2;
			G.drawLine(R.x, R.y, R.x+R.width, R.y+R.height);
			G.drawLine(R.x, R.y, R.x+headSize, R.y);
			G.drawLine(R.x, R.y, R.x, R.y+headSize);
		}
	}
	public void drawTile(Graphics G, int x, int y) {
		G.drawRect((int) toScreenX(x), (int) toScreenY(y), (int) zoom, (int) zoom);
	}
	public void drawActor(Graphics G, int x, int y) {
		G.drawArc((int) toScreenX(x), (int) toScreenY(y), (int) zoom, (int) zoom, 0, 360);
	}
	public void drawTiles(Graphics G, Set<Point> S) {
		Iterator<Point> itr = S.iterator();
		Point			P;
		
		while(itr.hasNext()) {
			P = itr.next();
			drawTile(G, P.x, P.y);
		}
	}
	public void drawActors(Graphics G, Set<Point> S) {
		Iterator<Point> itr = S.iterator();
		Point			P;
		
		while(itr.hasNext()) {
			P = itr.next();
			drawActor(G, P.x, P.y);
		}
	}

	public void fillTile(Graphics G, int x, int y) {
		G.fillRect((int) toScreenX(x), (int) toScreenY(y), (int) zoom, (int) zoom);
	}
	public void fillActor(Graphics G, int x, int y) {
		G.fillArc((int) toScreenX(x), (int) toScreenY(y), (int) zoom, (int) zoom, 0, 360);
	}
	public void fillTiles(Graphics G, Set<Point> S) {
		if (S.size() < cameraArea()) {
			Iterator<Point> itr = S.iterator();
			Point			P;
			
			while(itr.hasNext()) {
				P = itr.next();
				fillTile(G, P.x, P.y);
			}
		} else {
			Set<Point>	C = DrawSet.getRect(
					(int) Math.floor(cameraX()), 
					(int) Math.floor(cameraY()), 
					(int) Math.ceil(cameraW()), 
					(int) Math.ceil(cameraH())
			);
			Iterator<Point> itr = C.iterator();
			Point			P;
			while(itr.hasNext()) {
				P = itr.next();
				if (S.contains(P)) {
					fillTile(G, P.x,P.y);
				}
			}
		}
	}
	public void fillActors(Graphics G, Set<Point> S) {
		Iterator<Point> itr = S.iterator();
		Point			P;
		
		while(itr.hasNext()) {
			P = itr.next();
			fillActor(G, P.x, P.y);
		}
	}

	public void drawColumn(Graphics G, int x) {
		G.drawRect((int) toScreenX(x), 0, (int) toScreenX(x), getPaintComponent().getHeight());
		G.drawRect((int) toScreenX(x+zoom), 0, (int) toScreenX(x+zoom), getPaintComponent().getHeight());
	}
	public void drawRow(Graphics G, int y) {
		G.drawRect(0, (int) toScreenY(y), getPaintComponent().getWidth(), (int) toScreenY(y));
		G.drawRect(0, (int) toScreenY(y+zoom), getPaintComponent().getWidth(), (int) toScreenY(y+zoom));
	}
	
	//
	// other useful methods
	//
	public Collection<Sprite>	getAllSprites() {
//		return getSprites().values();
		return getSprites();
	}
	public Collection<Sprite>	getSelectedSprites() {
		Iterator<Sprite>	itr = getAllSprites().iterator();
		ArrayList<Sprite>	list = new ArrayList<Sprite>();
		Sprite				S;
		while(itr.hasNext()) {
			S = itr.next();
			if (S.isSelected()) {
				list.add(S);
			}
		}
		return list;
	}
	public Collection<Sprite>	getUnselectedSprites() {
		Iterator<Sprite>	itr = getAllSprites().iterator();
		ArrayList<Sprite>	list = new ArrayList<Sprite>();
		Sprite				S;
		while(itr.hasNext()) {
			S = itr.next();
			if (!S.isSelected()) {
				list.add(S);
			}
		}
		return list;
	}

	
	//
	// Screen Drawing methods
	//
	public void DrawMouse(Graphics G) {
		Rectangle MR = new Rectangle();
		MR.x		= (int) mScreenPos.getX();
		MR.y		= (int) mScreenPos.getY();
		MR.width	= (int) 20;
		MR.height	= (int) 20;
	}
	public void DrawGrid(Graphics G) {
		Color	oldColor  = G.getColor();
		
		int	cx = (int) Math.floor(cameraX());
		int	cy = (int) Math.floor(cameraY());
		int	x, y;
		
		G.setColor(getGridColor());
		
		for(x = 0; x < cameraW()+1; x++) {
			G.drawLine((int) toScreenX(cx+x), 0, (int) toScreenX(cx+x), getPaintComponent().getHeight());
		}
		
		for(y = 0; y < cameraH()+1; y++) {
			G.drawLine(0, (int) toScreenY(cy+y), getPaintComponent().getWidth(), (int) toScreenY(cy+y));
		}
		
		G.setColor(oldColor);
	}
	public void DrawSelected(Graphics G) {
		Color	oldColor = G.getColor();
		G.setXORMode(getSelectedColor());
		fillTiles(G,selected);
		G.setPaintMode();
		G.setColor(oldColor);
	}
	public void DeleteAllSprites() {
		getSprites().clear();
		refresh();
	}
	public void DeleteSelectedSprites() {
		Iterator<Sprite>	itr = getAllSprites().iterator();
		Sprite				S;
		while(itr.hasNext()) {
			S = itr.next();
			if (S.isSelected()) {
				itr.remove();
				
			}
		}
		refresh();
	}
	public void DeselectAllSprites() {
		Iterator<Sprite>	itr = getAllSprites().iterator();
		Sprite				S;
		
		while(itr.hasNext()) {
			S = itr.next();
			S.setSelected(false);
			this.firePropertyChange(SPRITE_REMOVED_PROPERTY, S, null);
		}
	}
	
	public void DrawSprites(Graphics G) {
//		Iterator<Sprite>	itr = getSprites().values().iterator();
		Iterator<Sprite>	itr = getAllSprites().iterator();
		Sprite				S;
		Rectangle			R;
		
		while(itr.hasNext()) {
			S = itr.next();
			R = getTile(S.getPos());
			S.paintPoint(G, R);
		}
	}
	
	
	@Override
	public void paint(Graphics G) {
		Rectangle R = getPaintComponent().getVisibleRect();
		
		Color		oldColor = G.getColor();
		G.setColor(getBackground());
		G.fillRect(R.x,R.y,R.width,R.height);
		G.setColor(oldColor);
		
		
		DrawGrid(G);
		DrawSprites(G);
		DrawSelected(G);
		DrawMouse(G);
		
		super.paintChildren(G);
	}
	
	@Override
	public void mousePressed(MouseEvent e) {
		super.mousePressed(e);
		mCanvasPos = this.toTile((int) mScreenPos.getX(), (int) mScreenPos.getY());
		mCanvasDown.put(e.getButton(), mCanvasPos);
		mOrigonDown.put(e.getButton(), getOrigon());
		
	}
	
	@Override 
	public void mouseReleased(MouseEvent e) {
		super.mouseMoved(e);
		
		mCanvasPos = this.toTile((int) mScreenPos.getX(), (int) mScreenPos.getY());
		mCanvasUp.put(e.getButton(), mCanvasPos);
		mOrigonUp.put(e.getButton(), getOrigon());
		
	}
	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		super.mouseWheelMoved(e);
		mCanvasPos = this.toTile((int) mScreenPos.getX(), (int) mScreenPos.getY());

	}
	@Override
	public void mouseMoved(MouseEvent e) {
		super.mouseMoved(e);
		mCanvasPos = this.toTile((int) mScreenPos.getX(), (int) mScreenPos.getY());
	}
	@Override
	public void mouseDragged(MouseEvent e) {
		super.mouseDragged(e);
		mCanvasPos = this.toTile((int) mScreenPos.getX(), (int) mScreenPos.getY());
	}
	public boolean isDrawMouse() {
		return drawMouse;
	}
	public void setDrawMouse(boolean drawMouse) {
		this.drawMouse = drawMouse;
	}
	public ArrayList<Sprite>	getSprites() {
		return Sprite.all;
	}

	
}








